#include <stdlib.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <math.h>



#include "global.h"
#include "constantes.h"
#include "sockets.h"
#include "configuracion.h"
#include "listas.h"
#include "NIPC.h"



int Sincronizar(struct DiscoConocido * DiscoASincronizar, struct DiscoConocido * DiscoALeer);
int VerificarSITodosLosDAEstanListos();
int VerificarExistenciaDiscoSincronizados();
int RecorrerListaDePedidosYSacarlosDeLPUT( struct DiscoConocido * pDisco);
void intacad ( int n , char* cadena);
void * HiloDeDisco (void * pNodoDisco);



void * HiloDeDisco (void * pNodoDisco)
{

   struct ListaIDdescriptors * NodoDescriptores;
   struct DiscoConocido * pDisco;
   struct DiscoConocido * pDiscoALeer;
   char PayloadDescriptor;
   char sDescriptorID[20];
   char sIDdescriptor_delPedido[20];
   char ssocket[20];
   char sSocket[20];
   char sSector1[100];
   char sSector2[100];
   char sBuffer[1050];
   int EsLaPrimeraVez = 1;
   int iMayorSocket;
   int CodSalSinc;
   fd_set set_socketsHiloParaDisco;
   fd_set set_socketsDesconec;	
	InfoIPC infoIPC;

   pDisco = (struct DiscoConocido *) pNodoDisco;
   printf("Entre a hilo de disco \n");
 
   if(pDisco->Estado == SINCRONIZANDO)
   {
		while(1)
		{
      
         pDiscoALeer = BuscarDiscoListo();  
			if(pDiscoALeer == NULL)
			{
				if( VerificarExistenciaDiscoSincronizados() == 0)   
				{
               printf("Entre a hilo de disco \n");
               generarStructIPC("nada",TERMINAR_CONEXION,&infoIPC);
               enviarIPC( pDisco->iSocketHDAParaArray,&infoIPC);
               recibirIPC(pDisco->iSocketHDAParaArray,&infoIPC);
               return NULL;
				}
				else
					continue;
         }
        
         pDiscoALeer->Estado = SINCRONIZANDO_LEYENDO;
			printf("Entre a hilo de disco sadsadsad\n");
         
         /*
         CodSalSinc = Sincronizar(pDisco, pDiscoALeer);
         if( CodSalSinc == -1)
				continue;
			else if( CodSalSinc == -3)
			{
				pDiscoALeer->Estado = LISTO;
				SubEstado = SINCRONIZADO;
				return NULL;
			}
         */
         
         
			pDiscoALeer->Estado = LISTO;
			pDisco->Estado = LISTO;

         printf("DISCO LISTO\n");
         
         if(VerificarSITodosLosDAEstanListos() == VERDADERO)
				SubEstado = SINCRONIZADO;
         
         free(pDiscoALeer);
         
			break;

		}
	}
			
   while(1)
   {
      FD_ZERO(&set_socketsHiloParaDisco);
      FD_SET(pDisco->iSocketHDAParaArray , &set_socketsHiloParaDisco);
      iMayorSocket = pDisco->iSocketHDAParaArray;
      FD_SET(pDisco->iSocketHDAParaDA , &set_socketsHiloParaDisco);
      if(pDisco->iSocketHDAParaDA > iMayorSocket)	
         iMayorSocket = pDisco->iSocketHDAParaDA;
      FD_ZERO(&set_socketsDesconec);
      FD_SET(pDisco->iSocketHDAParaDA , &set_socketsDesconec);

      select(iMayorSocket + 1 , &set_socketsHiloParaDisco, NULL , &set_socketsDesconec, NULL);	/*queda bloqueado hasta que pase algo en alguno de los sockets*/

      
      /******RTA DEL DISCO*******/

      if( FD_ISSET(pDisco->iSocketHDAParaDA , &set_socketsHiloParaDisco ) > 0 )	
      {
         infoIPC.payLoadDescriptor = NADA;
         recibirIPC(pDisco->iSocketHDAParaDA, &infoIPC);  /*aca esta el buffer con el resultado*/
         strcpy(sBuffer,infoIPC.payload);
         
         
         if(infoIPC.payLoadDescriptor == NADA)
         {
            /*se desconecto el disco**/
            /**LIBERAR PEDIDOS AL DISCO**/
            if (VerificarExistenciaDiscoSincronizados() <= 1) /*si devueve cero quiere decir que no encontro MAS discos sincronizados*/
            {
               /*EL array pasa a estar CORRUPTO,entonces se desconecta del VSK*/
              
               generarStructIPC("nada", TERMINAR_CONEXION,&infoIPC);
               enviarIPC( pDisco->iSocketHDAParaArray,&infoIPC);
               recibirIPC(pDisco->iSocketHDAParaArray, &infoIPC);
               cerrarSocket(pDisco->iSocketArrayParaHDA);
               cerrarSocket(pDisco->iSocketHDAParaArray);
               cerrarSocket(pDisco->iSocketHDAParaDA);
               SacarDeLista_DA(&pDisco);       /*solo desenalzar el nodo de la lista ListaDA , sin liberar*/
               free(pDisco);
               return NULL;

            }

            RecorrerListaDePedidosYSacarlosDeLPUT( pDisco );  /**recorre la lista de pedidos del disco, y saca los que se encuentren en la lista PUT. y decrementa discos a espejar**/
            cerrarSocket(pDisco->iSocketHDAParaArray);
            cerrarSocket(pDisco->iSocketHDAParaDA);
            return NULL;

         }
      
         NodoDescriptores = BuscarDescriptorEnListaDeDisco(pDisco->ListaDeIDPedidos, sDescriptorID, DEL_DA);   //busca en lista de descrip el sDescriptorID_delDA que sea = sDescriptorID 
         SacarNodoDescriptoresDeLista(&NodoDescriptores, &(pDisco->ListaDeIDPedidos), &(pDisco->ListaDeIDPedidos_Ult));
         
         /******Envio respuesta del pedido al ARRAY****/ 
         
         generarStructIPC(NodoDescriptores->sDescriptorID_delPedido, infoIPC.payLoadDescriptor,&infoIPC);
         enviarIPC( pDisco->iSocketHDAParaArray,&infoIPC);
         
         generarStructIPC(NodoDescriptores->sSocketPorElCualResponder, infoIPC.payLoadDescriptor,&infoIPC);
         enviarIPC( pDisco->iSocketHDAParaArray,&infoIPC);
         
         generarStructIPC(sBuffer, infoIPC.payLoadDescriptor,&infoIPC);
         enviarIPC( pDisco->iSocketHDAParaArray,&infoIPC);
         
         free(NodoDescriptores);
      
      }


      /****PEDIDO DEL ARRAY*****/
      if( FD_ISSET(pDisco->iSocketHDAParaArray , &set_socketsHiloParaDisco ) > 0 )	
      {  
         memset(sBuffer, '\0', sizeof(sBuffer) );

         recibirIPC(pDisco->iSocketHDAParaDA, &infoIPC); /*recibo IDdescriptor_delPedido*/
         strcpy(sIDdescriptor_delPedido,infoIPC.payLoadDescriptor);
         recibirIPC(pDisco->iSocketHDAParaDA, &infoIPC); /*recibo ssocket*/
         strcpy(ssocket,infoIPC.payLoadDescriptor);
               
         NodoDescriptores = malloc(sizeof(struct ListaIDdescriptors));			

         if(infoIPC.payLoadDescriptor == GET_SECTORES)
         {
            memset(sSector1, '\0', sizeof(sSector1) );
         
            recibirIPC(pDisco->iSocketHDAParaArray,&infoIPC);
            strcpy(sSector1,infoIPC.payLoadDescriptor);
            
            if(strcmp(sSector1,"") == 0){
               generarStructIPC("nada",infoIPC.payLoadDescriptor,&infoIPC);
               enviarIPC(pDisco->iSocketHDAParaDA,&infoIPC);
            }
            
            else{
               generarStructIPC(sSector1,infoIPC.payLoadDescriptor,&infoIPC);
               enviarIPC(pDisco->iSocketHDAParaDA,&infoIPC);
            }
                 
         }
         else /*PUT_SECTORES*/
         {
            printf("Proximamanete put sectores \n");		
         }
         
         strcpy(NodoDescriptores->sDescriptorID_delPedido , sIDdescriptor_delPedido);
         strcpy(NodoDescriptores->sSocketPorElCualResponder , sSocket);		
         NodoDescriptores->Sgt = NULL;
         NodoDescriptores->Ant = NULL;    
         AgregarAColaDePedidosADisco(&NodoDescriptores, &(pDisco->ListaDeIDPedidos), &(pDisco->ListaDeIDPedidos_Ult) );
         
         free(NodoDescriptores);
		
      }
   }

   return NULL;

}

int Sincronizar(struct DiscoConocido * DiscoASincronizar, struct DiscoConocido * DiscoALeer)
{


	struct timeval tvTiempoCero;
	int iSocketParaLeer;

	int iSector1, iSector2;
	int iSectorAEscribir;
	int iCantidadDeSectores;
	char Buffer[1024];
	char sEstrucEscritura[1040]; 
	char sAux[100];
	char PayloadDescriptor;
	char sDescriptorID[20];
	InfoIPC *infoIPC;
	
	PuertoParaEscucharPedidosPorSincro=23000;
	
	fd_set set_socketDiscoSinc;
	tvTiempoCero.tv_sec = 0;
	tvTiempoCero.tv_usec = 0;

   printf("Proximamente sera la sincronizacion \n");
	return 0;
   
   iCantidadDeSectores = glChs.cilindros * glChs.cabezal * glChs.sectores;


	for( iSectorAEscribir=0; iSectorAEscribir < iCantidadDeSectores ; iSectorAEscribir++)
	{
	    iSocketParaLeer = PrepararSocket (-1, sIP_Local);
	    Conectar (iSocketParaLeer , sIP_Local , PuertoParaEscucharPedidosPorSincro);
	    
		
		generarStructIPC(DiscoALeer->sNombre,GET_SECTORES,infoIPC);
		enviarIPC( iSocketParaLeer,infoIPC); /*SE LE ENVIA PRIMERO EL NOMBRE DEL DISCO QUE SE TIENE QUE LEER*/
	   
		
		iSector1 = iSectorAEscribir;
	    intacad(iSectorAEscribir ,sAux);
	    
		generarStructIPC(sAux,GET_SECTORES,infoIPC);
		enviarIPC( iSocketParaLeer,infoIPC);
    
	    iSectorAEscribir++;
	    iSector2 = iSectorAEscribir;
	    
		if(iSectorAEscribir == iCantidadDeSectores)
	    {
			generarStructIPC("nada",GET_SECTORES,infoIPC);
			enviarIPC( iSocketParaLeer,infoIPC);
			
	    }
	    
	    else
	    {
			intacad(iSectorAEscribir ,sAux);
			generarStructIPC(sAux,GET_SECTORES,infoIPC);
			enviarIPC( iSocketParaLeer,infoIPC);
	    }
	    
	    recibirIPC(iSocketParaLeer,infoIPC);
	    if(infoIPC->payLoadDescriptor == NO_EXISTE)
	    {
			cerrarSocket(iSocketParaLeer);
			return -1;
	    }
		recibirIPC(iSocketParaLeer, infoIPC);
	    generarStructIPC("nada",infoIPC->payLoadDescriptor,infoIPC);
		enviarIPC( iSocketParaLeer,infoIPC);/*se le envia otro ipc para indicarle que ya puede cerrar la conexion*/
	    cerrarSocket(iSocketParaLeer);

	    memset(sEstrucEscritura, '\0' , sizeof(sEstrucEscritura));
	    memcpy(sEstrucEscritura, &iSector1, /*4*/ sizeof(int));
	    memcpy(sEstrucEscritura + /*4*/ sizeof(int), Buffer, 512 );
	    memcpy(sEstrucEscritura + /*4*/ sizeof(int)+ 512, &iSector2, /*4*/ sizeof(int));
	    memcpy(sEstrucEscritura + /*4*/ sizeof(int)+ 512 + /*4*/ sizeof(int) , Buffer+512, 512 );

 
		FD_ZERO(&set_socketDiscoSinc);
        FD_SET(DiscoASincronizar->iSocketHDAParaDA , &set_socketDiscoSinc);
        select(DiscoASincronizar->iSocketHDAParaDA + 1 , &set_socketDiscoSinc, NULL , NULL, &tvTiempoCero);	/* no queda bloqueado */
            
	    if( FD_ISSET(DiscoASincronizar->iSocketHDAParaDA , &set_socketDiscoSinc ) > 0 )
        {
			printf("El disco que se estaba sincronizando se desconecto\n");
			RecorrerListaDePedidosYSacarlosDeLPUT( DiscoASincronizar );  /**recorre la lista de pedidos del disco, y saca los que se encuentren en la lista PUT. y decrementa discos a espejar**/
			cerrarSocket(DiscoASincronizar->iSocketHDAParaArray);
			cerrarSocket(DiscoASincronizar->iSocketHDAParaDA);

			return -3;

        }

		generarStructIPC(sEstrucEscritura,PUT_SECTORES,infoIPC);

	    if(enviarIPC(DiscoASincronizar->iSocketHDAParaDA,infoIPC) == -1) /*se le envia el buffer */
        {
			printf("El disco que se estaba sincronizando se desconecto\n");
			RecorrerListaDePedidosYSacarlosDeLPUT( DiscoASincronizar );  /**recorre la lista de pedidos del disco, y saca los que se encuentren en la lista PUT. y decrementa discos a espejar**/
			cerrarSocket(DiscoASincronizar->iSocketHDAParaArray);
			cerrarSocket(DiscoASincronizar->iSocketHDAParaDA);
			
			return -3;
	    }
                
                
        infoIPC->payLoadDescriptor = NADA;
		recibirIPC(DiscoASincronizar->iSocketHDAParaDA, &infoIPC);        
	    
		if(infoIPC->payLoadDescriptor == NADA)
	    {
			printf("El disco que se estaba sincronizando se desconecto\n");
			RecorrerListaDePedidosYSacarlosDeLPUT( DiscoASincronizar );  /**recorre la lista de pedidos del disco, y saca los que se encuentren en la lista PUT. y decrementa discos a espejar**/
			cerrarSocket(DiscoASincronizar->iSocketHDAParaArray);
			cerrarSocket(DiscoASincronizar->iSocketHDAParaDA);

		return -3;
	    }

	    printf("Sincronizando: Sector %i Copiado\r\n", iSectorAEscribir);
	}
	
   
return 0;
}



int VerificarSITodosLosDAEstanListos()
{
    struct DiscoConocido * pAuxDisco;

    pAuxDisco = ListaDiscos;
    while(pAuxDisco != NULL)
    {
	if (pAuxDisco->Estado != LISTO)
	    return FALSO;

	pAuxDisco = pAuxDisco->Sgt;
    }

    return VERDADERO;
}


int VerificarExistenciaDiscoSincronizados()
{
    struct DiscoConocido * pAuxDisco;
    int icantidad = 0;
	InfoIPC *infoIPC;
	
    pAuxDisco = ListaDiscos;
    while(pAuxDisco != NULL)
    {
	if ((pAuxDisco->Estado == LISTO)  || (pAuxDisco->Estado == SINCRONIZANDO_LEYENDO))
	    icantidad ++;
	pAuxDisco = pAuxDisco->Sgt;
    }

    return icantidad;
}



int RecorrerListaDePedidosYSacarlosDeLPUT( struct DiscoConocido * pDisco)
{
    char PayloadDescriptor;
    char sDescriptorID[22];
    struct ListaIDdescriptors * pNodo;
    struct Pedido_PutSectores * pNodoPut;
	InfoIPC *infoIPC;


    pNodo = pDisco->ListaDeIDPedidos;
    while(pNodo != NULL)
    {
		pNodoPut = NULL;
		pNodoPut = BuscarDescriptorYDevolverNodoDePedidoPut(pNodo->sDescriptorID_delPedido);		/*busca un la lista ListaPedidos_Put el nodo con el sDescriptorID que sea igual a IDdescriptor_dePedido (sin sacar el nodo de la lista )*/
		if (pNodoPut != NULL)
		{
			PayloadDescriptor = RTA_PUT_SECTORES;
			
			
			generarStructIPC(pNodo->sDescriptorID_delPedido ,PayloadDescriptor,infoIPC);
			enviarIPC(pDisco->iSocketHDAParaArray,infoIPC);
			generarStructIPC(pNodo->sSocketPorElCualResponder,PayloadDescriptor,infoIPC);
			enviarIPC(pDisco->iSocketHDAParaArray,infoIPC);
			generarStructIPC(NULL,PayloadDescriptor,infoIPC);
			enviarIPC(pDisco->iSocketHDAParaArray,infoIPC);
			generarStructIPC(NULL,pNodo->cCerrarONoSocket,infoIPC);
			enviarIPC(pDisco->iSocketHDAParaArray,infoIPC);
			
		}
		pNodo = pNodo->Sgt;
    }  
    
    return 0;

	
}



void intacad ( int n , char* cadena)
{	
    int j=0;
    int i=0;
	char aux;

	do
    {
		cadena[i] = (n % 10) + '0';    
		n=n/10;
		i++;
	}while (n > 0);

    cadena[i] = '\0';
    i--;

	while(i>j)
	{
		aux=cadena[i];
		cadena[i]=cadena[j];
		cadena[j]=aux;
		i--;
		j++;
	}

}



